package com.violet.myorm.core.condition;

import java.util.ArrayList;
import java.util.List;

public class LambdaWhere<E> implements Where<E, LambdaWhere<E>> {
    private final List<String> where;
    private final List<Object> whereParams;

    public LambdaWhere() {
        this.where = new ArrayList<>();
        this.whereParams = new ArrayList<>();
    }

    public LambdaWhere<E> join(SerializableFunction<E, ?> function, String joinCondition, Object... values) {
        return join(getFieldName(function), joinCondition, values);
    }

    @Override
    public List<String> getWhere() {
        return where;
    }

    @Override
    public List<Object> getWhereParams() {
        return whereParams;
    }

    @Override
    public <T> LambdaWhere<E> gt(String fieldName, T value) {
        return join(fieldName, " > ? ", value);
    }

    @Override
    public <T> LambdaWhere<E> gte(String fieldName, T value) {
        return join(fieldName, " >= ? ", value);
    }

    @Override
    public <T> LambdaWhere<E> lt(String fieldName, T value) {
        return join(fieldName, " < ? ", value);
    }

    @Override
    public <T> LambdaWhere<E> lte(String fieldName, T value) {
        return join(fieldName, " <= ? ", value);
    }

    @Override
    public <T> LambdaWhere<E> between(String fieldName, T value1, T value2) {
        return join(fieldName, " BETWEEN ? AND ? ", value1, value2);
    }

    @Override
    public <T> LambdaWhere<E> eq(String fieldName, T value) {
        return join(fieldName, " = ? ", value);
    }

    @Override
    public <T> LambdaWhere<E> ne(String fieldName, T value) {
        return join(fieldName, " != ? ", value);
    }

    @Override
    public LambdaWhere<E> like(String fieldName, String value) {
        value = "%" + value + "%";
        return join(fieldName, " LIKE ? ", value);
    }

    @Override
    public <T> LambdaWhere<E> in(String fieldName, T... values) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < values.length; i++) {
            if (i == 0) sb.append(" IN (");
            else sb.append(", ");
            sb.append("?");
        }
        sb.append(")");
        where.add(fieldName + sb);
        whereParams.addAll(List.of(values));
        return this;
    }

    @Override
    public <T> LambdaWhere<E> notIn(String fieldName, T... values) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < values.length; i++) {
            if (i == 0) sb.append(" NOT IN (");
            else sb.append(", ");
            sb.append("?");
        }
        sb.append(")");
        where.add(fieldName + sb);
        whereParams.addAll(List.of(values));
        return this;
    }

    @Override
    public <T> LambdaWhere<E> notNull(String fieldName) {
        return join(fieldName, " IS NOT NULL ");
    }

    @Override
    public <T> LambdaWhere<E> isNull(String fieldName) {
        return join(fieldName, " IS NULL ");
    }

    public <T> LambdaWhere<E> gt(SerializableFunction<E, T> function, T value) {
        return gt(getFieldName(function), value);
    }

    public <T> LambdaWhere<E> gte(SerializableFunction<E, T> function, T value) {
        return gte(getFieldName(function), value);
    }

    public <T> LambdaWhere<E> lt(SerializableFunction<E, T> function, T value) {
        return lt(getFieldName(function), value);
    }

    public <T> LambdaWhere<E> lte(SerializableFunction<E, T> function, T value) {
        return lte(getFieldName(function), value);
    }

    public <T> LambdaWhere<E> between(SerializableFunction<E, T> function, T value1, T value2) {
        return between(getFieldName(function), value1, value2);
    }

    public <T> LambdaWhere<E> eq(SerializableFunction<E, T> function, T value) {
        return eq(getFieldName(function), value);
    }

    public <T> LambdaWhere<E> ne(SerializableFunction<E, T> function, T value) {
        return ne(getFieldName(function), value);
    }

    public LambdaWhere<E> like(SerializableFunction<E, String> function, String value) {
        return like(getFieldName(function), value);
    }

    public <T> LambdaWhere<E> in(SerializableFunction<E, T> function, T... values) {
        return in(getFieldName(function), values);
    }

    public <T> LambdaWhere<E> notIn(SerializableFunction<E, T> function, T... values) {
        return notIn(getFieldName(function), values);
    }

    public LambdaWhere<E> and() {
        where.add(" AND ");
        return this;
    }

    public LambdaWhere<E> or() {
        where.add(" OR ");
        return this;
    }

    public LambdaWhere<E> not() {
        where.add(" NOT ");
        return this;
    }

    public LambdaWhere<E> notNull(SerializableFunction<E, ?> function) {
        return notNull(getFieldName(function));
    }

    public LambdaWhere<E> isNull(SerializableFunction<E, ?> function) {
        return isNull(getFieldName(function));
    }

    public LambdaWhere<E> andEq(String fieldName, Object value) {
        if (where.isEmpty()) where.add(fieldName + " = ? ");
        else where.add("AND " + fieldName + " = ? ");
        whereParams.add(value);
        return this;
    }
}
